package com.susu.zookeeper.curator.config;

import com.susu.zookeeper.curator.annotations.ZkLock;
import com.susu.zookeeper.curator.component.ZookeeperComponent;
import com.susu.zookeeper.curator.model.ZookeeperLock;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.Objects;

/**
 * 锁的切面
 *
 * @author 苏苏
 * @date 2020/11/6 11:21
 **/
@Slf4j
@Configuration
@Aspect
public class LockAspect {

    /**
     * 用于SpEL表达式解析.
     */
    private SpelExpressionParser parser = new SpelExpressionParser();

    /**
     * 用于获取方法参数定义名字.
     */
    private DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();

    @Autowired
    private ZookeeperComponent zookeeperComponent;

    @Around("@annotation(com.susu.zookeeper.curator.annotations.ZkLock)")
    public Object aroundReturning(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature sign = (MethodSignature) joinPoint.getSignature();
        Method method = sign.getMethod();
        ZkLock zkLock = method.getAnnotation(ZkLock.class);
        if (zkLock == null) {
            return joinPoint.proceed();
        }
        log.info("[zk start]zk锁开始");
        //获取key
        String lockKey = getLockKey(joinPoint, zkLock);
        log.info("[zk ...]lockKey:{}", lockKey);

        //key为null直接返回
        if (StringUtils.isEmpty(lockKey)) {
            log.info("[zk end]lockKey为空");
            return joinPoint.proceed();
        }

        log.info("[zk ...]获取锁对象");
        //获取锁
        ZookeeperLock zookeeperLock = zookeeperComponent.createZookeeperLock(zkLock.lockType(), lockKey);
        log.info("[zk ...]获取锁对象成功");

        Object proceed;
        boolean success = false;
        try {
            success = zkLock.time() >= 0 ? zookeeperLock.acquire() : zookeeperLock.acquire(zkLock.time(), zkLock.unit());
            log.info("[zk ...]加锁结果:{}", success);
            proceed = joinPoint.proceed();
        } finally {
            if (success) {
                log.info("[zk end]释放锁");
                zookeeperLock.release();
            }
        }
        return proceed;
    }


    /**
     * 获取表达式Key
     *
     * @param joinPoint joinPoint
     * @param zkLock    zkLock
     * @return lockKey
     */
    private String getLockKey(ProceedingJoinPoint joinPoint, ZkLock zkLock) {
        try {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            //获取方法参数
            String[] paramNames = nameDiscoverer.getParameterNames(signature.getMethod());
            //设置EvaluationContext
            EvaluationContext context = new StandardEvaluationContext();
            Object[] args = joinPoint.getArgs();
            for (int i = 0; i < args.length; i++) {
                //添值
                context.setVariable(paramNames[i], args[i]);
            }
            //条件表达式
            String condition = zkLock.condition();
            Boolean canGetKey = true;
            if (!StringUtils.isEmpty(condition)) {
                Expression conditionExpression = parser.parseExpression(condition);
                canGetKey = conditionExpression.getValue(context, Boolean.class);
            }
            //不满足条件无法获取key,此时不进行锁的操作
            if (Objects.isNull(canGetKey) || !canGetKey) {
                return null;
            }

            //获取key
            String key = zkLock.key();
            if (StringUtils.isEmpty(key)) {
                //默认全名+方法名+参数名
                return getClassAndMethodNameAndParamsName(signature.getMethod(), paramNames);
            }

            //解析key的表达式
            Expression keyExpression = parser.parseExpression(key);
            return keyExpression.getValue(context, String.class);
        } catch (Exception e) {
            log.error("获取表达式key失败,异常:", e);
        }
        return null;
    }


    /**
     * 获取方法类全名+方法名+参数名
     *
     * @param method 方法
     * @return 类全名+方法名+参数名
     */
    private String getClassAndMethodNameAndParamsName(Method method, String[] paramNames) {
        String names = String.join("-", paramNames);
        //获取类全名
        String className = method.getDeclaringClass().getName();
        //获取方法名
        String methodName = method.getName();
        StringBuilder path = new StringBuilder(className);
        path.append("-").append(methodName).append("-").append(names);
        return path.toString();
    }
}
